/* 
 * File:   Nivel.cpp
 * Author: fibrizo
 * 
 * Created on 24 de mayo de 2014, 19:13
 */

#include "server_Nivel.h"
#include <string>
#include <vector>
#include "server_MaterialFisico.h"
#include "server_Tierra.h"
#include "server_Caja.h"
#include "server_Metal.h"
#include "server_Bomba.h"
#include "server_Mapa.h"
#include "server_Bomberman.h"
#include "server_Planta.h"
#include "server_Enemigo.h"
#include "server_ZombieVerde.h"
#include "server_ZombieRojo.h"
#include "server_ZombieAzul.h"
#include "server_RandomGenerator.h"
#include <iostream>


using std::string;
using std::vector;
using std::cout;
using std::endl;

/***********************************************************************/
/*                              CONSTRUCTOR                            */

/***********************************************************************/

Nivel::Nivel() {
    this->mapa = Mapa();
    this->inicializado = false;
    this->terminado = false;
    this->cantZombiesAzules = 0;
    this->cantZombiesRojos = 0;
    this->cantZombiesVerdes = 0;
}

Nivel::Nivel(int tamanio, string tileMap) {
    this->terminado = false;
    Mapa mapaVacio = this->mapa;
    this->mapa = Mapa(tamanio);
    this->inicializado = false;
    this->cantZombiesAzules = 0;
    this->cantZombiesRojos = 0;
    this->cantZombiesVerdes = 0;
    this->tileMap = tileMap;

}

Nivel::Nivel(const Nivel& orig) {
    this->terminado = orig.terminado;
    this->mapa = orig.mapa;
    this->inicializado = orig.inicializado;
    this->cantZombiesAzules = orig.cantZombiesAzules;
    this->cantZombiesRojos = orig.cantZombiesRojos;
    this->cantZombiesVerdes = orig.cantZombiesVerdes;
    this->tileMap = orig.tileMap;

}

/***********************************************************************/
/*                            METODOS PRIVADOS                         */

/***********************************************************************/

void Nivel::actualizar() {

    for (vector<Bomberman*>::iterator iter = personajes.begin(); iter != personajes.end(); iter++) {
        if (!(*iter)->estaVivo()) {
            mapa.sacarElemento((*iter), (*iter)->getPosicion());
            iter = personajes.erase(iter);
        }
        if (iter == personajes.end()) break;
        if ((*iter)->listoParaColocarBomba()) {
            (*iter)->crearBomba();
            Bomba* bomba = (*iter)->colocarBomba();
            bombas.push_back(bomba);
            mapa.ubicarElemento(bomba, bomba->getPosicion());
        }
        mapa.moverElemento((*iter));
    }

    for (unsigned int i = 0; i < plantas.size(); i++) {
        Bomba* bombaPlanta = (plantas[i])->disparar();
        if (bombaPlanta) {
            bombas.push_back(bombaPlanta);
        }
    }


    for (vector<Enemigo*>::iterator iter = enemigos.begin(); iter != enemigos.end(); iter++) {
        if (!(*iter)->estaVivo()) {
            Enemigo* aux = (*iter);
            mapa.sacarElemento(aux, aux->getPosicion());
            iter = enemigos.erase(iter);
            delete aux;
        }
        if (iter == enemigos.end()) break;
        (*iter)->setDireccion();
        Enemigo* enemigoActual = (*iter);
        if (!enemigoActual->estaEscondido())
            mapa.moverElemento(enemigoActual);
    }

    list<Bomba*>::iterator it = bombas.begin();
    while (it != bombas.end()) {
        (*it)->actuar(mapa);
        if ((*it)->yaExploto()) {
            Bomba* aux = (*it);
            mapa.sacarElemento(aux, aux->getPosicion());
            it = bombas.erase(it);
            delete aux;
        } else {
            it++;
        }
    }
    mapa.limpiarExplosiones();
    for (unsigned int i = 0; i < personajes.size(); i++) {
        this->mapa.resolverColisiones(personajes[i]);
    }

    if ((personajes.size() == 0) || (enemigos.size() == 0))
        terminado = true;
}

bool Nivel::termino() {
    return terminado;
}

/***********************************************************************/
/*                            METODOS PUBLICOS                         */

/***********************************************************************/

void Nivel::setZombies(size_t verdes, size_t azules, size_t rojos) {
    this->cantZombiesVerdes = verdes;
    this->cantZombiesAzules = azules;
    this->cantZombiesRojos = rojos;

    for (size_t i = 0; i < verdes; i++) {
        ZombieVerde* nuevoVerde = new ZombieVerde();
        (this->enemigos).push_back(nuevoVerde);
    }
    for (size_t i = 0; i < azules; i++) {
        ZombieAzul* nuevoAzul = new ZombieAzul();
        (this->enemigos).push_back(nuevoAzul);
    }
    for (size_t i = 0; i < rojos; i++) {
        ZombieRojo* nuevoRojo = new ZombieRojo();
        (this->enemigos).push_back(nuevoRojo);
    }
}

void Nivel::inicializar(string tileMap, vector<Bomberman*> listaDeBombermans) {
    this->terminado = false;
    mapa.inicializar();
    const char* cadenaMapa = tileMap.c_str();
    int i = 0;
    int j = 0;
    int x = 0;
    int indiceBombermans = 0;
    int tam = mapa.getTamanio();
    Coordenada coord = Coordenada(0, 0);
    while (x < tam) {
        j = 0;
        while (j < tam) {
            if (cadenaMapa[i] == CAJA) {
                mapa.establecerMaterial(new Caja(), coord);
            } else if (cadenaMapa[i] == CAJA_CON_ZOMBIE) {
                mapa.establecerMaterial(new Caja(), coord);
                ZombieVerde* zombie = new ZombieVerde();
                zombie->ubicar(coord);
                enemigos.push_back(zombie);
                mapa.ubicarElemento(zombie, coord);
                zombie->esconder();
                this->cantZombiesVerdes++;
            } else if (cadenaMapa[i] == METAL) {
                mapa.establecerMaterial(new Metal(), coord);
            } else {
                if (cadenaMapa[i] == ZOMBIE_VERDE) {
                    ZombieVerde* zombie = new ZombieVerde();
                    zombie->ubicar(coord);
                    enemigos.push_back(zombie);
                    mapa.ubicarElemento(zombie, coord);
                    this->cantZombiesVerdes++;
                } else if (cadenaMapa[i] == ZOMBIE_ROJO) {
                    ZombieRojo* zombie = new ZombieRojo();
                    zombie->ubicar(coord);
                    enemigos.push_back(zombie);
                    mapa.ubicarElemento(zombie, coord);
                    this->cantZombiesRojos++;
                } else if (cadenaMapa[i] == ZOMBIE_AZUL) {
                    ZombieAzul* zombie = new ZombieAzul();
                    zombie->ubicar(coord);
                    enemigos.push_back(zombie);
                    mapa.ubicarElemento(zombie, coord);
                    this->cantZombiesAzules++;
                } else if (cadenaMapa[i] == PLANTA) {
                    Planta* planta = new Planta(establecerDireccionPlanta(coord), coord);
                    plantas.push_back(planta);
                    mapa.ubicarElemento(planta, coord);
                } else if ((cadenaMapa[i] >= COMIENZO_BOMBERMANS) && (cadenaMapa[i] <= FIN_BOMBERMANS)) {
                    listaSpawnBomberman.push_back(coord);
                    listaDeBombermans[indiceBombermans]->ubicar(coord);
                    mapa.ubicarElemento(listaDeBombermans[indiceBombermans], coord);
                    this->personajes.push_back(listaDeBombermans[indiceBombermans]);
                    indiceBombermans++;
                }
            }
            coord.incrementarY();
            j++;
            i++;
        }
        coord = Coordenada(coord.getX(), 0);
        coord.incrementarX();
        x++;
    }
    inicializado = true;
}


// Metodo privado de Nivel. En base a una coordenada de una planta
// devuelve a que direccion deberia apuntar.

string Nivel::establecerDireccionPlanta(Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    RandomGenerator generador;
    int tam = mapa.getTamanio();
    vector<string> dir = {"arriba", "abajo", "derecha", "izquierda"};
    vector<string> primerCuadrante = {"abajo", "derecha"};
    vector<string> segundoCuadrante = {"abajo", "izquierda"};
    vector<string> tercerCuadrante = {"arriba", "derecha"};
    vector<string> cuartoCuadrante = {"arriba", "izquierda"};
    if ((x < tam / 2) && (y > tam / 2)) return primerCuadrante[generador.generate(0, 1)];
    else if ((x > tam / 2) && (y > tam / 2)) return segundoCuadrante[generador.generate(0, 1)];
    else if ((x < tam / 2) && (y < tam / 2)) return tercerCuadrante[generador.generate(0, 1)];
    else if ((x < tam / 2) && (y < tam / 2)) return cuartoCuadrante[generador.generate(0, 1)];
    else return dir[generador.generate(0, 3)];
}



/***********************************************************************/
/*                              DESTRUCTOR                             */

/***********************************************************************/


Nivel::~Nivel() {
    while (!enemigos.empty()) {
        Enemigo* aux = enemigos.back();
        enemigos.pop_back();
        delete aux;
    }
    while (!plantas.empty()) {
        Planta* aux = plantas.back();
        plantas.pop_back();
        delete aux;
    }
    while (!bombas.empty()) {
        Bomba* aux = bombas.back();
        bombas.pop_back();
        delete aux;
    }
}

string Nivel::getTileMap() {
    if (this->inicializado)
        return this->mapa.generarTileMap();
    return tileMap;
}

size_t Nivel::getCantEnemigos() {
    size_t cantEnemigos = enemigos.size();
    return cantEnemigos;
}

size_t Nivel::getCantPlantas() {
    return this->plantas.size();
}

size_t Nivel::getCantBombermans() {
    return this->personajes.size();
}

bool Nivel::ganado() {
    if ((enemigos.size() == 0) && (terminado)) {
        return true;
    }
    return false;
}